home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkTextMark.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-31  |  22.0 KB  |  782 lines

  1. /* 
  2.  * tkTextMark.c --
  3.  *
  4.  *    This file contains the procedure that implement marks for
  5.  *    text widgets.
  6.  *
  7.  * Copyright (c) 1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tkTextMark.c 1.15 96/02/15 18:52:59
  14.  */
  15.  
  16. #include "tkInt.h"
  17. #include "tkText.h"
  18. #include "tkPort.h"
  19.  
  20. /*
  21.  * Macro that determines the size of a mark segment:
  22.  */
  23.  
  24. #define MSEG_SIZE ((unsigned) (Tk_Offset(TkTextSegment, body) \
  25.     + sizeof(TkTextMark)))
  26.  
  27. /*
  28.  * Forward references for procedures defined in this file:
  29.  */
  30.  
  31. static void        InsertUndisplayProc _ANSI_ARGS_((TkText *textPtr,
  32.                 TkTextDispChunk *chunkPtr));
  33. static int        MarkDeleteProc _ANSI_ARGS_((TkTextSegment *segPtr,
  34.                 TkTextLine *linePtr, int treeGone));
  35. static TkTextSegment *    MarkCleanupProc _ANSI_ARGS_((TkTextSegment *segPtr,
  36.                 TkTextLine *linePtr));
  37. static void        MarkCheckProc _ANSI_ARGS_((TkTextSegment *segPtr,
  38.                 TkTextLine *linePtr));
  39. static int        MarkLayoutProc _ANSI_ARGS_((TkText *textPtr,
  40.                 TkTextIndex *indexPtr, TkTextSegment *segPtr,
  41.                 int offset, int maxX, int maxChars,
  42.                 int noCharsYet, Tk_Uid wrapMode,
  43.                 TkTextDispChunk *chunkPtr));
  44. static int        MarkFindNext _ANSI_ARGS_((Tcl_Interp *interp,
  45.                 TkText *textPtr, char *markName));
  46. static int        MarkFindPrev _ANSI_ARGS_((Tcl_Interp *interp,
  47.                 TkText *textPtr, char *markName));
  48.  
  49.  
  50. /*
  51.  * The following structures declare the "mark" segment types.
  52.  * There are actually two types for marks, one with left gravity
  53.  * and one with right gravity.  They are identical except for
  54.  * their gravity property.
  55.  */
  56.  
  57. Tk_SegType tkTextRightMarkType = {
  58.     "mark",                    /* name */
  59.     0,                        /* leftGravity */
  60.     (Tk_SegSplitProc *) NULL,            /* splitProc */
  61.     MarkDeleteProc,                /* deleteProc */
  62.     MarkCleanupProc,                /* cleanupProc */
  63.     (Tk_SegLineChangeProc *) NULL,        /* lineChangeProc */
  64.     MarkLayoutProc,                /* layoutProc */
  65.     MarkCheckProc                /* checkProc */
  66. };
  67.  
  68. Tk_SegType tkTextLeftMarkType = {
  69.     "mark",                    /* name */
  70.     1,                        /* leftGravity */
  71.     (Tk_SegSplitProc *) NULL,            /* splitProc */
  72.     MarkDeleteProc,                /* deleteProc */
  73.     MarkCleanupProc,                /* cleanupProc */
  74.     (Tk_SegLineChangeProc *) NULL,        /* lineChangeProc */
  75.     MarkLayoutProc,                /* layoutProc */
  76.     MarkCheckProc                /* checkProc */
  77. };
  78.  
  79. /*
  80.  *--------------------------------------------------------------
  81.  *
  82.  * TkTextMarkCmd --
  83.  *
  84.  *    This procedure is invoked to process the "mark" options of
  85.  *    the widget command for text widgets. See the user documentation
  86.  *    for details on what it does.
  87.  *
  88.  * Results:
  89.  *    A standard Tcl result.
  90.  *
  91.  * Side effects:
  92.  *    See the user documentation.
  93.  *
  94.  *--------------------------------------------------------------
  95.  */
  96.  
  97. int
  98. TkTextMarkCmd(textPtr, interp, argc, argv)
  99.     register TkText *textPtr;    /* Information about text widget. */
  100.     Tcl_Interp *interp;        /* Current interpreter. */
  101.     int argc;            /* Number of arguments. */
  102.     char **argv;        /* Argument strings.  Someone else has already
  103.                  * parsed this command enough to know that
  104.                  * argv[1] is "mark". */
  105. {
  106.     int c, i;
  107.     size_t length;
  108.     Tcl_HashEntry *hPtr;
  109.     TkTextSegment *markPtr;
  110.     Tcl_HashSearch search;
  111.     TkTextIndex index;
  112.     Tk_SegType *newTypePtr;
  113.  
  114.     if (argc < 3) {
  115.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  116.         argv[0], " mark option ?arg arg ...?\"", (char *) NULL);
  117.     return TCL_ERROR;
  118.     }
  119.     c = argv[2][0];
  120.     length = strlen(argv[2]);
  121.     if ((c == 'g') && (strncmp(argv[2], "gravity", length) == 0)) {
  122.     if (argc > 5) {
  123.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  124.             argv[0], " mark gravity markName ?gravity?",
  125.             (char *) NULL);
  126.         return TCL_ERROR;
  127.     }
  128.     hPtr = Tcl_FindHashEntry(&textPtr->markTable, argv[3]);
  129.     if (hPtr == NULL) {
  130.         Tcl_AppendResult(interp, "there is no mark named \"",
  131.             argv[3], "\"", (char *) NULL);
  132.         return TCL_ERROR;
  133.     }
  134.     markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  135.     if (argc == 4) {
  136.         if (markPtr->typePtr == &tkTextRightMarkType) {
  137. #ifdef STk_CODE
  138.         interp->result = "\"right\"";
  139.         } else {
  140.         interp->result = "\"left\"";
  141. #else
  142.         interp->result = "right";
  143.         } else {
  144.         interp->result = "left";
  145. #endif
  146.         }
  147.         return TCL_OK;
  148.     }
  149.     length = strlen(argv[4]);
  150.     c = argv[4][0];
  151.     if ((c == 'l') && (strncmp(argv[4], "left", length) == 0)) {
  152.         newTypePtr = &tkTextLeftMarkType;
  153.     } else if ((c == 'r') && (strncmp(argv[4], "right", length) == 0)) {
  154.         newTypePtr = &tkTextRightMarkType;
  155.     } else {
  156.         Tcl_AppendResult(interp, "bad mark gravity \"",
  157.             argv[4], "\": must be left or right", (char *) NULL);
  158.         return TCL_ERROR;
  159.     }
  160.     TkTextMarkSegToIndex(textPtr, markPtr, &index);
  161.     TkBTreeUnlinkSegment(textPtr->tree, markPtr,
  162.         markPtr->body.mark.linePtr);
  163.     markPtr->typePtr = newTypePtr;
  164.     TkBTreeLinkSegment(markPtr, &index);
  165.     } else if ((c == 'n') && (strncmp(argv[2], "names", length) == 0)) {
  166.     if (argc != 3) {
  167.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  168.             argv[0], " mark names\"", (char *) NULL);
  169.         return TCL_ERROR;
  170.     }
  171.     for (hPtr = Tcl_FirstHashEntry(&textPtr->markTable, &search);
  172.         hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  173.         Tcl_AppendElement(interp,
  174.             Tcl_GetHashKey(&textPtr->markTable, hPtr));
  175.     }
  176.     } else if ((c == 'n') && (strncmp(argv[2], "next", length) == 0)) {
  177.     if (argc != 4) {
  178.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  179.             argv[0], " mark next index\"", (char *) NULL);
  180.         return TCL_ERROR;
  181.     }
  182.     return MarkFindNext(interp, textPtr, argv[3]);
  183.     } else if ((c == 'p') && (strncmp(argv[2], "previous", length) == 0)) {
  184.     if (argc != 4) {
  185.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  186.             argv[0], " mark previous index\"", (char *) NULL);
  187.         return TCL_ERROR;
  188.     }
  189.     return MarkFindPrev(interp, textPtr, argv[3]);
  190.     } else if ((c == 's') && (strncmp(argv[2], "set", length) == 0)) {
  191.     if (argc != 5) {
  192.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  193.             argv[0], " mark set markName index\"", (char *) NULL);
  194.         return TCL_ERROR;
  195.     }
  196.     if (TkTextGetIndex(interp, textPtr, argv[4], &index) != TCL_OK) {
  197.         return TCL_ERROR;
  198.     }
  199.     TkTextSetMark(textPtr, argv[3], &index);
  200.     } else if ((c == 'u') && (strncmp(argv[2], "unset", length) == 0)) {
  201.     for (i = 3; i < argc; i++) {
  202.         hPtr = Tcl_FindHashEntry(&textPtr->markTable, argv[i]);
  203.         if (hPtr != NULL) {
  204.         markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  205.         if ((markPtr == textPtr->insertMarkPtr)
  206.             || (markPtr == textPtr->currentMarkPtr)) {
  207.             continue;
  208.         }
  209.         TkBTreeUnlinkSegment(textPtr->tree, markPtr,
  210.             markPtr->body.mark.linePtr);
  211.         Tcl_DeleteHashEntry(hPtr);
  212.         ckfree((char *) markPtr);
  213.         }
  214.     }
  215.     } else {
  216.     Tcl_AppendResult(interp, "bad mark option \"", argv[2],
  217.         "\": must be gravity, names, next, previous, set, or unset",
  218.         (char *) NULL);
  219.     return TCL_ERROR;
  220.     }
  221.     return TCL_OK;
  222. }
  223.  
  224. /*
  225.  *----------------------------------------------------------------------
  226.  *
  227.  * TkTextSetMark --
  228.  *
  229.  *    Set a mark to a particular position, creating a new mark if
  230.  *    one doesn't already exist.
  231.  *
  232.  * Results:
  233.  *    The return value is a pointer to the mark that was just set.
  234.  *
  235.  * Side effects:
  236.  *    A new mark is created, or an existing mark is moved.
  237.  *
  238.  *----------------------------------------------------------------------
  239.  */
  240.  
  241. TkTextSegment *
  242. TkTextSetMark(textPtr, name, indexPtr)
  243.     TkText *textPtr;        /* Text widget in which to create mark. */
  244.     char *name;            /* Name of mark to set. */
  245.     TkTextIndex *indexPtr;    /* Where to set mark. */
  246. {
  247.     Tcl_HashEntry *hPtr;
  248.     TkTextSegment *markPtr;
  249.     TkTextIndex insertIndex;
  250.     int new;
  251.  
  252.     hPtr = Tcl_CreateHashEntry(&textPtr->markTable, name, &new);
  253.     markPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  254.     if (!new) {
  255.     /*
  256.      * If this is the insertion point that's being moved, be sure
  257.      * to force a display update at the old position.  Also, don't
  258.      * let the insertion cursor be after the final newline of the
  259.      * file.
  260.      */
  261.  
  262.     if (markPtr == textPtr->insertMarkPtr) {
  263.         TkTextIndex index, index2;
  264.         TkTextMarkSegToIndex(textPtr, textPtr->insertMarkPtr, &index);
  265.         TkTextIndexForwChars(&index, 1, &index2);
  266.         TkTextChanged(textPtr, &index, &index2);
  267.         if (TkBTreeLineIndex(indexPtr->linePtr)
  268.             == TkBTreeNumLines(textPtr->tree))  {
  269.         TkTextIndexBackChars(indexPtr, 1, &insertIndex);
  270.         indexPtr = &insertIndex;
  271.         }
  272.     }
  273.     TkBTreeUnlinkSegment(textPtr->tree, markPtr,
  274.         markPtr->body.mark.linePtr);
  275.     } else {
  276.     markPtr = (TkTextSegment *) ckalloc(MSEG_SIZE);
  277.     markPtr->typePtr = &tkTextRightMarkType;
  278.     markPtr->size = 0;
  279.     markPtr->body.mark.textPtr = textPtr;
  280.     markPtr->body.mark.linePtr = indexPtr->linePtr;
  281.     markPtr->body.mark.hPtr = hPtr;
  282.     Tcl_SetHashValue(hPtr, markPtr);
  283.     }
  284.     TkBTreeLinkSegment(markPtr, indexPtr);
  285.  
  286.     /*
  287.      * If the mark is the insertion cursor, then update the screen at the
  288.      * mark's new location.
  289.      */
  290.  
  291.     if (markPtr == textPtr->insertMarkPtr) {
  292.     TkTextIndex index2;
  293.  
  294.     TkTextIndexForwChars(indexPtr, 1, &index2);
  295.     TkTextChanged(textPtr, indexPtr, &index2);
  296.     }
  297.     return markPtr;
  298. }
  299.  
  300. /*
  301.  *--------------------------------------------------------------
  302.  *
  303.  * TkTextMarkSegToIndex --
  304.  *
  305.  *    Given a segment that is a mark, create an index that
  306.  *    refers to the next text character (or other text segment
  307.  *    with non-zero size) after the mark.
  308.  *
  309.  * Results:
  310.  *    *IndexPtr is filled in with index information.
  311.  *
  312.  * Side effects:
  313.  *    None.
  314.  *
  315.  *--------------------------------------------------------------
  316.  */
  317.  
  318. void
  319. TkTextMarkSegToIndex(textPtr, markPtr, indexPtr)
  320.     TkText *textPtr;        /* Text widget containing mark. */
  321.     TkTextSegment *markPtr;    /* Mark segment. */
  322.     TkTextIndex *indexPtr;    /* Index information gets stored here.  */
  323. {
  324.     TkTextSegment *segPtr;
  325.  
  326.     indexPtr->tree = textPtr->tree;
  327.     indexPtr->linePtr = markPtr->body.mark.linePtr;
  328.     indexPtr->charIndex = 0;
  329.     for (segPtr = indexPtr->linePtr->segPtr; segPtr != markPtr;
  330.         segPtr = segPtr->nextPtr) {
  331.     indexPtr->charIndex += segPtr->size;
  332.     }
  333. }
  334.  
  335. /*
  336.  *--------------------------------------------------------------
  337.  *
  338.  * TkTextMarkNameToIndex --
  339.  *
  340.  *    Given the name of a mark, return an index corresponding
  341.  *    to the mark name.
  342.  *
  343.  * Results:
  344.  *    The return value is TCL_OK if "name" exists as a mark in
  345.  *    the text widget.  In this case *indexPtr is filled in with
  346.  *    the next segment whose after the mark whose size is
  347.  *    non-zero.  TCL_ERROR is returned if the mark doesn't exist
  348.  *    in the text widget.
  349.  *
  350.  * Side effects:
  351.  *    None.
  352.  *
  353.  *--------------------------------------------------------------
  354.  */
  355.  
  356. int
  357. TkTextMarkNameToIndex(textPtr, name, indexPtr)
  358.     TkText *textPtr;        /* Text widget containing mark. */
  359.     char *name;            /* Name of mark. */
  360.     TkTextIndex *indexPtr;    /* Index information gets stored here. */
  361. {
  362.     Tcl_HashEntry *hPtr;
  363.  
  364.     hPtr = Tcl_FindHashEntry(&textPtr->markTable, name);
  365.     if (hPtr == NULL) {
  366.     return TCL_ERROR;
  367.     }
  368.     TkTextMarkSegToIndex(textPtr, (TkTextSegment *) Tcl_GetHashValue(hPtr),
  369.         indexPtr);
  370.     return TCL_OK;
  371. }
  372.  
  373. /*
  374.  *--------------------------------------------------------------
  375.  *
  376.  * MarkDeleteProc --
  377.  *
  378.  *    This procedure is invoked by the text B-tree code whenever
  379.  *    a mark lies in a range of characters being deleted.
  380.  *
  381.  * Results:
  382.  *    Returns 1 to indicate that deletion has been rejected.
  383.  *
  384.  * Side effects:
  385.  *    None (even if the whole tree is being deleted we don't
  386.  *    free up the mark;  it will be done elsewhere).
  387.  *
  388.  *--------------------------------------------------------------
  389.  */
  390.  
  391.     /* ARGSUSED */
  392. static int
  393. MarkDeleteProc(segPtr, linePtr, treeGone)
  394.     TkTextSegment *segPtr;        /* Segment being deleted. */
  395.     TkTextLine *linePtr;        /* Line containing segment. */
  396.     int treeGone;            /* Non-zero means the entire tree is
  397.                      * being deleted, so everything must
  398.                      * get cleaned up. */
  399. {
  400.     return 1;
  401. }
  402.  
  403. /*
  404.  *--------------------------------------------------------------
  405.  *
  406.  * MarkCleanupProc --
  407.  *
  408.  *    This procedure is invoked by the B-tree code whenever a
  409.  *    mark segment is moved from one line to another.
  410.  *
  411.  * Results:
  412.  *    None.
  413.  *
  414.  * Side effects:
  415.  *    The linePtr field of the segment gets updated.
  416.  *
  417.  *--------------------------------------------------------------
  418.  */
  419.  
  420. static TkTextSegment *
  421. MarkCleanupProc(markPtr, linePtr)
  422.     TkTextSegment *markPtr;        /* Mark segment that's being moved. */
  423.     TkTextLine *linePtr;        /* Line that now contains segment. */
  424. {
  425.     markPtr->body.mark.linePtr = linePtr;
  426.     return markPtr;
  427. }
  428.  
  429. /*
  430.  *--------------------------------------------------------------
  431.  *
  432.  * MarkLayoutProc --
  433.  *
  434.  *    This procedure is the "layoutProc" for mark segments.
  435.  *
  436.  * Results:
  437.  *    If the mark isn't the insertion cursor then the return
  438.  *    value is -1 to indicate that this segment shouldn't be
  439.  *    displayed.  If the mark is the insertion character then
  440.  *    1 is returned and the chunkPtr structure is filled in.
  441.  *
  442.  * Side effects:
  443.  *    None, except for filling in chunkPtr.
  444.  *
  445.  *--------------------------------------------------------------
  446.  */
  447.  
  448.     /*ARGSUSED*/
  449. static int
  450. MarkLayoutProc(textPtr, indexPtr, segPtr, offset, maxX, maxChars,
  451.     noCharsYet, wrapMode, chunkPtr)
  452.     TkText *textPtr;        /* Text widget being layed out. */
  453.     TkTextIndex *indexPtr;    /* Identifies first character in chunk. */
  454.     TkTextSegment *segPtr;    /* Segment corresponding to indexPtr. */
  455.     int offset;            /* Offset within segPtr corresponding to
  456.                  * indexPtr (always 0). */
  457.     int maxX;            /* Chunk must not occupy pixels at this
  458.                  * position or higher. */
  459.     int maxChars;        /* Chunk must not include more than this
  460.                  * many characters. */
  461.     int noCharsYet;        /* Non-zero means no characters have been
  462.                  * assigned to this line yet. */
  463.     Tk_Uid wrapMode;        /* Not used. */
  464.     register TkTextDispChunk *chunkPtr;
  465.                 /* Structure to fill in with information
  466.                  * about this chunk.  The x field has already
  467.                  * been set by the caller. */
  468. {
  469.     if (segPtr != textPtr->insertMarkPtr) {
  470.     return -1;
  471.     }
  472.  
  473.     chunkPtr->displayProc = TkTextInsertDisplayProc;
  474.     chunkPtr->undisplayProc = InsertUndisplayProc;
  475.     chunkPtr->measureProc = (Tk_ChunkMeasureProc *) NULL;
  476.     chunkPtr->bboxProc = (Tk_ChunkBboxProc *) NULL;
  477.     chunkPtr->numChars = 0;
  478.     chunkPtr->minAscent = 0;
  479.     chunkPtr->minDescent = 0;
  480.     chunkPtr->minHeight = 0;
  481.     chunkPtr->width = 0;
  482.  
  483.     /*
  484.      * Note: can't break a line after the insertion cursor:  this
  485.      * prevents the insertion cursor from being stranded at the end
  486.      * of a line.
  487.      */
  488.  
  489.     chunkPtr->breakIndex = -1;
  490.     chunkPtr->clientData = (ClientData) textPtr;
  491.     return 1;
  492. }
  493.  
  494. /*
  495.  *--------------------------------------------------------------
  496.  *
  497.  * TkTextInsertDisplayProc --
  498.  *
  499.  *    This procedure is called to display the insertion
  500.  *    cursor.
  501.  *
  502.  * Results:
  503.  *    None.
  504.  *
  505.  * Side effects:
  506.  *    Graphics are drawn.
  507.  *
  508.  *--------------------------------------------------------------
  509.  */
  510.  
  511.     /* ARGSUSED */
  512. void
  513. TkTextInsertDisplayProc(chunkPtr, x, y, height, baseline, display, dst, screenY)
  514.     TkTextDispChunk *chunkPtr;        /* Chunk that is to be drawn. */
  515.     int x;                /* X-position in dst at which to
  516.                      * draw this chunk (may differ from
  517.                      * the x-position in the chunk because
  518.                      * of scrolling). */
  519.     int y;                /* Y-position at which to draw this
  520.                      * chunk in dst (x-position is in
  521.                      * the chunk itself). */
  522.     int height;                /* Total height of line. */
  523.     int baseline;            /* Offset of baseline from y. */
  524.     Display *display;            /* Display to use for drawing. */
  525.     Drawable dst;            /* Pixmap or window in which to draw
  526.                      * chunk. */
  527.     int screenY;            /* Y-coordinate in text window that
  528.                      * corresponds to y. */
  529. {
  530.     TkText *textPtr = (TkText *) chunkPtr->clientData;
  531.     int halfWidth = textPtr->insertWidth/2;
  532.  
  533.     if ((x + halfWidth) <= 0) {
  534.     /*
  535.      * The insertion cursor is off-screen.  Just return.
  536.      */
  537.  
  538.     return;
  539.     }
  540.  
  541.     /*
  542.      * As a special hack to keep the cursor visible on mono displays
  543.      * (or anywhere else that the selection and insertion cursors
  544.      * have the same color) write the default background in the cursor
  545.      * area (instead of nothing) when the cursor isn't on.  Otherwise
  546.      * the selection might hide the cursor.
  547.      */
  548.  
  549.     if (textPtr->flags & INSERT_ON) {
  550.     Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->insertBorder,
  551.         x - textPtr->insertWidth/2, y, textPtr->insertWidth,
  552.         height, textPtr->insertBorderWidth, TK_RELIEF_RAISED);
  553.     } else if (textPtr->selBorder == textPtr->insertBorder) {
  554.     Tk_Fill3DRectangle(textPtr->tkwin, dst, textPtr->border,
  555.         x - textPtr->insertWidth/2, y, textPtr->insertWidth,
  556.         height, 0, TK_RELIEF_FLAT);
  557.     }
  558. }
  559.  
  560. /*
  561.  *--------------------------------------------------------------
  562.  *
  563.  * InsertUndisplayProc --
  564.  *
  565.  *    This procedure is called when the insertion cursor is no
  566.  *    longer at a visible point on the display.  It does nothing
  567.  *    right now.
  568.  *
  569.  * Results:
  570.  *    None.
  571.  *
  572.  * Side effects:
  573.  *    None.
  574.  *
  575.  *--------------------------------------------------------------
  576.  */
  577.  
  578.     /* ARGSUSED */
  579. static void
  580. InsertUndisplayProc(textPtr, chunkPtr)
  581.     TkText *textPtr;            /* Overall information about text
  582.                      * widget. */
  583.     TkTextDispChunk *chunkPtr;        /* Chunk that is about to be freed. */
  584. {
  585.     return;
  586. }
  587.  
  588. /*
  589.  *--------------------------------------------------------------
  590.  *
  591.  * MarkCheckProc --
  592.  *
  593.  *    This procedure is invoked by the B-tree code to perform
  594.  *    consistency checks on mark segments.
  595.  *
  596.  * Results:
  597.  *    None.
  598.  *
  599.  * Side effects:
  600.  *    The procedure panics if it detects anything wrong with
  601.  *    the mark.
  602.  *
  603.  *--------------------------------------------------------------
  604.  */
  605.  
  606. static void
  607. MarkCheckProc(markPtr, linePtr)
  608.     TkTextSegment *markPtr;        /* Segment to check. */
  609.     TkTextLine *linePtr;        /* Line containing segment. */
  610. {
  611.     Tcl_HashSearch search;
  612.     Tcl_HashEntry *hPtr;
  613.  
  614.     if (markPtr->body.mark.linePtr != linePtr) {
  615.     panic("MarkCheckProc: markPtr->body.mark.linePtr bogus");
  616.     }
  617.  
  618.     /*
  619.      * Make sure that the mark is still present in the text's mark
  620.      * hash table.
  621.      */
  622.  
  623.     for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.textPtr->markTable,
  624.         &search); hPtr != markPtr->body.mark.hPtr;
  625.         hPtr = Tcl_NextHashEntry(&search)) {
  626.     if (hPtr == NULL) {
  627.         panic("MarkCheckProc couldn't find hash table entry for mark");
  628.     }
  629.     }
  630. }
  631.  
  632. /*
  633.  *--------------------------------------------------------------
  634.  *
  635.  * MarkFindNext --
  636.  *
  637.  *    This procedure searches forward for the next mark.
  638.  *
  639.  * Results:
  640.  *    A standard Tcl result, which is a mark name or an empty string.
  641.  *
  642.  * Side effects:
  643.  *    None.
  644.  *
  645.  *--------------------------------------------------------------
  646.  */
  647.  
  648. static int
  649. MarkFindNext(interp, textPtr, string)
  650.     Tcl_Interp *interp;            /* For error reporting */
  651.     TkText *textPtr;            /* The widget */
  652.     char *string;            /* The starting index or mark name */
  653. {
  654.     TkTextIndex index;
  655.     Tcl_HashEntry *hPtr;
  656.     register TkTextSegment *segPtr;
  657.     int offset;
  658.  
  659.  
  660.     hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
  661.     if (hPtr != NULL) {
  662.     /*
  663.      * If given a mark name, return the next mark in the list of
  664.      * segments, even if it happens to be at the same character position.
  665.      */
  666.     segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  667.     TkTextMarkSegToIndex(textPtr, segPtr, &index);
  668.     segPtr = segPtr->nextPtr;
  669.     } else {
  670.     /*
  671.      * For non-mark name indices we want to return any marks that
  672.      * are right at the index.
  673.      */
  674.     if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
  675.         return TCL_ERROR;
  676.     }
  677.     for (offset = 0, segPtr = index.linePtr->segPtr; 
  678.         segPtr != NULL && offset < index.charIndex;
  679.         offset += segPtr->size,    segPtr = segPtr->nextPtr) {
  680.         /* Empty loop body */ ;
  681.     }
  682.     }
  683.     while (1) {
  684.     /*
  685.      * segPtr points at the first possible candidate,
  686.      * or NULL if we ran off the end of the line.
  687.      */
  688.     for ( ; segPtr != NULL ; segPtr = segPtr->nextPtr) {
  689.         if (segPtr->typePtr == &tkTextRightMarkType ||
  690.             segPtr->typePtr == &tkTextLeftMarkType) {
  691.         Tcl_SetResult(interp,
  692.             Tcl_GetHashKey(&textPtr->markTable, segPtr->body.mark.hPtr),
  693.             TCL_STATIC);
  694.         return TCL_OK;
  695.         }
  696.     }
  697.     index.linePtr = TkBTreeNextLine(index.linePtr);
  698.     if (index.linePtr == (TkTextLine *) NULL) {
  699.         return TCL_OK;
  700.     }
  701.     index.charIndex = 0;
  702.     segPtr = index.linePtr->segPtr;
  703.     }
  704. }
  705.  
  706. /*
  707.  *--------------------------------------------------------------
  708.  *
  709.  * MarkFindPrev --
  710.  *
  711.  *    This procedure searches backwards for the previous mark.
  712.  *
  713.  * Results:
  714.  *    A standard Tcl result, which is a mark name or an empty string.
  715.  *
  716.  * Side effects:
  717.  *    None.
  718.  *
  719.  *--------------------------------------------------------------
  720.  */
  721.  
  722. static int
  723. MarkFindPrev(interp, textPtr, string)
  724.     Tcl_Interp *interp;            /* For error reporting */
  725.     TkText *textPtr;            /* The widget */
  726.     char *string;            /* The starting index or mark name */
  727. {
  728.     TkTextIndex index;
  729.     Tcl_HashEntry *hPtr;
  730.     register TkTextSegment *segPtr, *seg2Ptr, *prevPtr;
  731.     int offset;
  732.  
  733.  
  734.     hPtr = Tcl_FindHashEntry(&textPtr->markTable, string);
  735.     if (hPtr != NULL) {
  736.     /*
  737.      * If given a mark name, return the previous mark in the list of
  738.      * segments, even if it happens to be at the same character position.
  739.      */
  740.     segPtr = (TkTextSegment *) Tcl_GetHashValue(hPtr);
  741.     TkTextMarkSegToIndex(textPtr, segPtr, &index);
  742.     } else {
  743.     /*
  744.      * For non-mark name indices we do not return any marks that
  745.      * are right at the index.
  746.      */
  747.     if (TkTextGetIndex(interp, textPtr, string, &index) != TCL_OK) {
  748.         return TCL_ERROR;
  749.     }
  750.     for (offset = 0, segPtr = index.linePtr->segPtr; 
  751.         segPtr != NULL && offset < index.charIndex;
  752.         offset += segPtr->size, segPtr = segPtr->nextPtr) {
  753.         /* Empty loop body */ ;
  754.     }
  755.     }
  756.     while (1) {
  757.     /*
  758.      * segPtr points just past the first possible candidate,
  759.      * or at the begining of the line.
  760.      */
  761.     for (prevPtr = NULL, seg2Ptr = index.linePtr->segPtr; 
  762.         seg2Ptr != NULL && seg2Ptr != segPtr;
  763.         seg2Ptr = seg2Ptr->nextPtr) {
  764.         if (seg2Ptr->typePtr == &tkTextRightMarkType ||
  765.             seg2Ptr->typePtr == &tkTextLeftMarkType) {
  766.         prevPtr = seg2Ptr;
  767.         }
  768.     }
  769.     if (prevPtr != NULL) {
  770.         Tcl_SetResult(interp, 
  771.         Tcl_GetHashKey(&textPtr->markTable, prevPtr->body.mark.hPtr),
  772.         TCL_STATIC);
  773.         return TCL_OK;
  774.     }
  775.     index.linePtr = TkBTreePreviousLine(index.linePtr);
  776.     if (index.linePtr == (TkTextLine *) NULL) {
  777.         return TCL_OK;
  778.     }
  779.     segPtr = NULL;
  780.     }
  781. }
  782.